goog.provide('mitel.presto');

goog.require('goog.Timer');
goog.require('goog.Uri.QueryData');
goog.require('goog.dom.forms');
goog.require('goog.dom.xml');
goog.require('goog.net.XhrIo');
goog.require('goog.structs.Map');
goog.require('goog.style');
goog.require('goog.ui.ac');
goog.require('mitel.presto.widget');
goog.require('mitel.request');
goog.require('mitel.deployment');

var requestMonitorTimer = new goog.Timer(3000);

var PRESTO_STATE_MAP = {
'IN_OFFICE': 'In Office',
'OUT_OF_OFFICE': 'Out of Office',
'UNKNOWN': 'Unknown'
};

var currPresence;

/**
 * @type {string} Hostname of the dingle server
 */
mitel.presto.DINGLE_SERVER = mitel.deployment.backend_server;

var main;


/**
 * @type {opensocial.Person}
 */
var viewer;


/**
 * Load the OpenSocial viewer object
 *
 * (July 2013) This was found not to be working anymore.
 * We will use a different technique to get the email logged-in user.
 *
var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), 'viewer');
req.send(function(data) {
  viewer = data.get('viewer').getData();
});
 */


/**
 * Constants for Dingle call control functions
 * @enum {string}
 */
mitel.presto.CallControl = {
  MAKECALL: 'MakeCall',
  CLEARCALL: 'ClearCall',
  HOLDCALL: 'HoldCall',
  RETRIEVECALL: 'RetrieveCall',
  CONFCALL: 'ConferenceCall',
  TRANSFERCALL: 'TransferCall',
  ALTERNATECALL: 'AlternateCall',
  PROMPTCALL: 'PromptCall',
  QUERYUSER: 'QueryUser',
  QUERYREQUESTSTATE: 'QueryRequestState'
};

/**
 * Contains the phone book of this user
 */
mitel.presto.phone_book = [];
mitel.presto.name_dict = [];

/**
 * Show the results of a contacts API query
 * @param {!Object} response API response.
 */
mitel.presto.showContactResults = function(response) {
  mitel.presto.showMain();
  goog.dom.removeChildren(main);

  /**
    * Shorten name to first initial and last name
    * @param {string} fullName The full name.
    * @return {string} Shortened name.
    */
  var shortenName = function(fullName) {
    var nameFragment = fullName.split(' ');
    var shortName = '';
    var lastIdx = (nameFragment.length - 1);
    var first = true;
    for (var i = 0; i < nameFragment.length; ++i) {
      if (first) {
        first = false;
      } else {
        shortName += ' ';
      }
      if (i == lastIdx) {
        shortName += nameFragment[i];
      } else {
        shortName += nameFragment[i].charAt(0) + '.';
      }
    }
    return shortName;
  }

  var people = [];
  mitel.presto.phone_book.length = 0;
  mitel.presto.name_dict.length = 0;
  for (var i = 0; i < response.data['feed']['entry'].length; i++) {
    var entry = response.data['feed']['entry'][i];
    var phoneNumbers = entry['gd$phoneNumber'];

    if (!phoneNumbers || phoneNumbers.length == 0) {
      continue;
    }

    for (var j = 0; j < phoneNumbers.length; j++) {
      var n = {
        number: entry['gd$phoneNumber'][j]['$t'],
        name: shortenName(entry['title']['$t'])
      };
      people.push(n);
      // Save it to the phone book to be used in searches later
      n.name = entry['title']['$t'];
      mitel.presto.phone_book.push(n);
      // Build up the name dictionary
      mitel.presto.name_dict.push(n.name);
    }
  }
  /* This has been replaced by the callbox
  var make_call = soy.renderAsElement(mitel.presto.widget.make_call, {people: people});

  goog.events.listen(make_call, goog.events.EventType.CHANGE,
      mitel.presto.invokeUserSelect);

  goog.dom.appendChild(main, make_call);
  */
};

mitel.presto.callbox_process = function(event)
{
    var names = goog.dom.getElement('callbox').value.toString().trim().replace(/,+$/, "").replace(/\S\s*,\s+\S/, "|");
    var list = [];

    if (names == "")
    {
        mitel.presto.callbox_reset();
        return;
    }
    // Build up the pattern by ORring all the names together. Trim and remove trailing commas also.
    var pattern = new RegExp(names, "i");

    // Now search through the phone book for matches
    for(var i=0; i<mitel.presto.phone_book.length; i++)
    {
        var entry = mitel.presto.phone_book[i];
        if (pattern.test(entry.name))
        {
            list.push(entry);
        }
    }
    for(var j=0; j<list.length; j++)
    {
        if(list[j].number.length > 0)
        {
            mitel.presto.invokeCallControl(mitel.presto.CallControl.MAKECALL, list[j].number);
        }
    }
};

mitel.presto.CALLBOX_SPLASH_PROMPT = "Mitel UCC";
mitel.presto.CALLBOX_IDLE_PROMPT = "Click2Call";
mitel.presto.CALLBOX_CALL_PROMPT = "Enter names to call";

mitel.presto.callbox_clear = function()
{
    goog.dom.getElement('callbox').value = "";
}

mitel.presto.callbox_reset = function()
{
    goog.dom.getElement('callbox').value = mitel.presto.CALLBOX_IDLE_PROMPT;
}

mitel.presto.callbox_mouseover = function(event)
{
    // Display prompt text
    if ( (event.currentTarget.value == "") ||
         (event.currentTarget.value == mitel.presto.CALLBOX_SPLASH_PROMPT) ||
         (event.currentTarget.value == mitel.presto.CALLBOX_IDLE_PROMPT) )
    {
        event.currentTarget.value = mitel.presto.CALLBOX_CALL_PROMPT;
    }
}

mitel.presto.callbox_focus = function(event)
{
    // Clear text if no name present
    if ( (event.currentTarget.value == mitel.presto.CALLBOX_SPLASH_PROMPT) ||
         (event.currentTarget.value == mitel.presto.CALLBOX_CALL_PROMPT) ||
         (event.currentTarget.value == mitel.presto.CALLBOX_IDLE_PROMPT) )
    {
        mitel.presto.callbox_clear();
    }
}

/**
 * Invoke the `MakeCall` call control function on the DN the user has selected
 * @param {goog.events.BrowserEvent} event Select box change event.
 */
mitel.presto.invokeUserSelect = function(event) {
  var select = event.currentTarget;

  var dn = goog.dom.$F(select);

  select.selectedIndex = 0;

  if (dn && dn.length > 0) {
    mitel.presto.invokeCallControl(mitel.presto.CallControl.MAKECALL, dn);
  }
};


/**
 * Sends a REST request to Dingle.
 * @param {mitel.presto.CallControl} func REST request to send.
 * @param {goog.structs.Map} data Parameters to be sent along with the request in the form of a Map,
 * null if no parameter is required.
 * @param {Function} callback Callback function to run upon response.
 */
mitel.presto.doCall = function(func, data, callback) {
  var postMap = new goog.structs.Map({
    'from': viewer.email,
    'fullName': viewer.full_name
  });

  if (data) {
    postMap.addAll(data);
  }

  var postData = goog.Uri.QueryData.createFromMap(postMap);

  goog.net.XhrIo.send(mitel.presto.DINGLE_SERVER + 'dingle/' + func,
      callback, 'POST', postData.toString());
};

mitel.presto.updateRequest = function(status) {
  goog.dom.setTextContent(goog.dom.getElement('request'), status ? status : '');
};

/**
 * Parses the request key from the Dingle response to a request
 * @param resp The response from a Dingle request
 */
mitel.presto.parseRequestKey = function(resp) {
  var xhr = resp.target;

  var dingle = goog.dom.xml.selectSingleNode(xhr.getResponseXml(), '//Dingle');
  return dingle.getAttribute('requestKey');
};

/**
 * Queries for the state of a particular request sent previously.
 * @param requestKey The unique key identifying the desired request
 */
mitel.presto.queryRequest = function(requestKey) {
  var params = new goog.structs.Map({
    'requestKey': requestKey
  });

  mitel.presto.doCall(mitel.presto.CallControl.QUERYREQUESTSTATE, params, function(r) {
    var requestKey = mitel.presto.parseRequestKey(r);

    if (!requestKey || (requestKey == '-1')) {
      mitel.presto.clearRequestMonitor();
      mitel.presto.updateRequest(null);
    } else {
      var xhr = r.target;
      var xml = xhr.getResponseXml();

      var request = goog.dom.xml.selectSingleNode(xml, '//Request');
      mitel.presto.updateRequest(request ? request.getAttribute('status') : '');
    }
  });
};


mitel.presto.clearRequestMonitor = function() {
  if (!requestMonitorTimer) {
    return;
  }

  requestMonitorTimer.stop();
  requestMonitorTimer = null;
};


mitel.presto.monitorRequest = function(requestKey) {
  // Huy: Don't do this anymore.
  return;
/*
  mitel.presto.clearRequestMonitor();
  requestMonitorTimer = new goog.Timer(3000);
  requestMonitorTimer.start();
  goog.events.listen(requestMonitorTimer, goog.Timer.TICK, function() {
    mitel.presto.queryRequest(requestKey);
  });
  requestMonitorTimer.dispatchTick();
*/
};

mitel.presto.invokeUpdatePresence = function()
{
  var radio_val = goog.dom.forms.getValueByName(goog.dom.getElement('presence1'), 'In_Out');
  var m = new goog.structs.Map({'presence': (radio_val == 'away')?'OUT_OF_OFFICE':'IN_OFFICE'});
  mitel.presto.doCall('UpdateUser', m, null/*no callback required*/);
};

/**
 * Invoke an actual action against Dingle
 * @param {mitel.presto.CallControl} func Function to call.
 * @param {string} toDn target phone number.
 */
mitel.presto.invokeCallControl = function(func, toDn) {
  var m = new goog.structs.Map({
    'toDn': toDn
  });

  mitel.presto.doCall(func, m, function(r) {
    var requestKey = mitel.presto.parseRequestKey(r);
    mitel.presto.monitorRequest(requestKey);
  });
};

/**
 * Invoke a call for the selected action
 * @param {SelectElement} action_select The action select box.
 */
mitel.presto.invokeActionSelect = function(action_select) {
  var action = goog.dom.$F(action_select);
  action_select.selectedIndex = 0;

  if (action.length > 0) {
    mitel.presto.invokeCallControl(action, '');
  }
};


/**
 * Invokes a prompt call for the input number
 * @param {string} text Input, presumably a phone number.
 */
mitel.presto.invokePromptCall = function(text) {
  var drop_target = goog.dom.getElement('drop_target');
  goog.style.showElement(drop_target, false);

  var map = new goog.structs.Map({
    target: text
  });

  mitel.presto.doCall(mitel.presto.CallControl.PROMPTCALL, map, function(data) {
    var prompttext = goog.dom.getElement('prompttext');
    var voice = goog.dom.getElement('voice');
    var video = goog.dom.getElement('video');

    goog.dom.removeChildren(prompttext);

    var xhr = data.target;
    var obj = xhr.getResponseJson();

    var entries = [];
    var numbers = [];
    for (var i = 0; i < obj.length; i++) {
      entries.push({contact: obj[i]['contact']});
      numbers.push(obj[i]['contact']);
    }

    var numbers_node = soy.renderAsElement(mitel.presto.widget.prompt_call, {entries: entries});
    goog.dom.appendChild(prompttext, numbers_node);

    goog.events.removeAll(voice);
    goog.events.listen(voice, goog.events.EventType.CLICK, function(event) {
      event.stopPropagation();
      mitel.presto.invokeCallControl(mitel.presto.CallControl.MAKECALL, numbers.join('|'));
      var msg = new gadgets.MiniMessage(window['__MODULE_ID__']);
      msg.createTimerMessage('Call to ' + numbers.join(', ') + ' initiated', 8);
      goog.style.showElement(drop_target, true);
      goog.dom.removeNode(numbers_node);
    });
	
	
    goog.events.removeAll(video);
    goog.events.listen(video, goog.events.EventType.CLICK, function(event) {
      event.stopPropagation();
      mitel.presto.invokeCallControl(mitel.presto.CallControl.MAKECALL, numbers.join('|'));
      var msg = new gadgets.MiniMessage(window['__MODULE_ID__']);
      msg.createTimerMessage('Call to ' + numbers.join(', ') + ' initiated', 8);
      goog.style.showElement(drop_target, true);
      goog.dom.removeNode(numbers_node);
    });
  });
};


/**
 * Fetch user's contact list from Google Contacts API, and then invoke
 * mite.presto.showContactResults.
 */
mitel.presto.fetchContactData = function() {
  url = 'http://www.google.com/m8/feeds/contacts/default/full?alt=json';
  mitel.request.makeRequest(url,
      mitel.presto.showContactResults,
      mitel.presto.hideMain);
};

mitel.presto.incall = false;
/**
 * Issues query user presence request
 */
mitel.presto.queryUserPresence = function() {

  mitel.presto.doCall(mitel.presto.CallControl.QUERYUSER, null, function(r) {
    var xhr = r.target;
    var xml = xhr.getResponseXml();

    var user = goog.dom.xml.selectSingleNode(xml, '//User');
    var presence = null;
    var presenceRaw = 'UNKNOWN';
    if (user && user.getAttribute('presence')) {
	  presenceRaw = user.getAttribute('presence');
    }
	presence = PRESTO_STATE_MAP[presenceRaw];
	
	if(!currPresence || (currPresence != presenceRaw))
	{
		switch(presenceRaw)
		{
			case "IN_OFFICE":
                                goog.dom.forms.setValue(goog.dom.getElement('radio_in'), true);
                                goog.dom.forms.setValue(goog.dom.getElement('radio_out'), false);
				mitel.sidebar_gadget.emit_presence(false);
			break;
					
			case "OUT_OF_OFFICE":
                                goog.dom.forms.setValue(goog.dom.getElement('radio_in'), false);
                                goog.dom.forms.setValue(goog.dom.getElement('radio_out'), true);
				mitel.sidebar_gadget.emit_presence(true);
			break;
			
			default:
                                goog.dom.forms.setValue(goog.dom.getElement('radio_in'), true);
                                goog.dom.forms.setValue(goog.dom.getElement('radio_out'), false);
				mitel.sidebar_gadget.emit_presence(false);
		}
	}

    var device = goog.dom.xml.selectSingleNode(xml, '//Device');
	var callPartyText;
	var dn = null;
	var stateHumanReadable = null;
    if (device) {
      mitel.presto.incall = true;
      var callParty = device.getAttribute('callParty');
      if (callParty.length > 0) {
        callPartyText = callParty;
      } else {
        callPartyText = false;
      }
      dn = device.getAttribute('dn');
      stateHumanReadable = device.getAttribute('stateHumanReadable');
    }
    else
    {
        // No device means no call. Clear the callbox if there was a call
        if(true == mitel.presto.incall)
        {
            mitel.presto.callbox_reset();
            mitel.presto.incall = false;
        }
    }

    var status_el = goog.dom.getElement('status');
    soy.renderElement(status_el, mitel.presto.widget.status, {
      device: device,
      callParty: callPartyText,
      dn: dn,
      stateHumanReadable: stateHumanReadable
    });
  });
};


/**
 * Shows the main Presto container
 */
mitel.presto.showMain = function() {
  if (!main)
    return;
  goog.style.showElement(main, true);
};


/**
 * Hides the main Presto container
 */
mitel.presto.hideMain = function() {
  if (!main)
    return;
  goog.style.showElement(main, false);
};


/**
 * Boot up Presto, performing DOM building and required initialization
 * @param {Object} parent_node The DOM node the application should attach
 *                 itself under.
 */
mitel.presto.boot = function(parent_node) {
  soy.renderElement(parent_node, mitel.presto.widget.ui);
  goog.style.setStyle(callprompt, 'display', 'none');
  goog.style.setStyle(drop_target, 'display', 'none');
  goog.style.setStyle(actions, 'display', 'none');
  main = goog.dom.getElement('main');
  mitel.presto.hideMain();

  // Call box
  var call_box = goog.dom.getElement('callbox');

  // Attach autocomplete to it
  var auto_complete = goog.ui.ac.createSimpleAutoComplete(mitel.presto.name_dict, call_box, true);
  // Attach listeners to it
  goog.events.listen(call_button, goog.events.EventType.CLICK,
      mitel.presto.callbox_process);
  goog.events.listen(call_box, goog.events.EventType.FOCUS,
      mitel.presto.callbox_focus);
  goog.events.listen(call_box, goog.events.EventType.MOUSEOVER,
      mitel.presto.callbox_mouseover);

  goog.events.listen(goog.dom.getElement('radio_in'), goog.events.EventType.CLICK, mitel.presto.invokeUpdatePresence);
  goog.events.listen(goog.dom.getElement('radio_out'), goog.events.EventType.CLICK, mitel.presto.invokeUpdatePresence);
  
  goog.events.listen(document.body,
      [goog.events.EventType.DRAGOVER,
       goog.events.EventType.DRAGENTER,
       goog.events.EventType.DROP],
      goog.events.Event.preventDefault);

  goog.events.listen(document.body, goog.events.EventType.DROP,
      function(event) {
        var txtContent = event.getBrowserEvent().dataTransfer.getData('text');
        mitel.presto.handleDropData(txtContent);
      });

  goog.events.listen(goog.dom.getElement('actions'),
      goog.events.EventType.CHANGE, function(event) {
        mitel.presto.invokeActionSelect(event.currentTarget);
      });

  var timer = new goog.Timer(60000);
  timer.start();
  goog.events.listen(timer, goog.Timer.TICK, mitel.presto.fetchContactData);
  timer.dispatchTick();
  
  timer = new goog.Timer(3000);
  timer.start();
  goog.events.listen(timer, goog.Timer.TICK, mitel.presto.queryUserPresence);
  timer.dispatchTick();

  var voice = goog.dom.getElement('voice');
  goog.events.listen(voice, goog.events.EventType.CLICK, function(event) {
    event.stopPropagation();
  });

  mitel.presto.queryUserPresence();
};


/**
 * Handle string that has been dropped in drop-zone
 * @param {!string} data Drop data (presumably a phone number).
 */
mitel.presto.handleDropData = function(data) {
  mitel.presto.invokePromptCall(data);
};
